import { _decorator, Node, find, isValid, Director, director, Widget, BlockInputEvents, } from "cc";
import { ResourceUtil } from "./resource_util";
import { UIMarqueePanel } from "../ui/common/ui_marquee_panel";
import { UIBase } from "../ui/common/ui_base";
import { UIDialogBase } from "../ui/common/ui_dialog_base";
import { UITipsBase } from "../ui/common/ui_tips_base";
import { EventProcessor } from "./client_event";
import { UIManagerDefines } from "./ui_manager_define";

/**
 * ui管理类
 */
export class UIManager {
    private _index: number = 0;     // uuid 序号
    private _panelMap: Map<string, { options: UIManagerDefines.PanelOptions, panel: Node }> = new Map(); // 面板地图
    private _loadPanelList: string[] = [];                       // 加载队列
    private _loadDialogList: string[] = [];                       // 加载队列
    private _dialogMap: Map<string, Node> = new Map();      // 弹窗地图
    private _marqueePanel: UIMarqueePanel | null = null;    // 跑马灯面板
    private _priorityPanelParentMap: Map<UIManagerDefines.PANELPRIORITY, Node> = new Map;    // 所有ui节点地图

    private _eventProcessor = new EventProcessor();

    private static _instance: UIManager;
    public static get instance () {
        if (this._instance) {
            return this._instance;
        }
        this._instance = new UIManager();
        return this._instance;
    }

    constructor () {
        // 监听切换场景事件
        director.on(Director.EVENT_BEFORE_SCENE_LAUNCH, this._clearPanels, this);
    }

    /**
     * 清除所有面板
     */
    private _clearPanels () {
        this._priorityPanelParentMap.forEach((node, type) => {
            if (type < UIManagerDefines.PANELPRIORITY.MARQUEE) {
                node.removeFromParent();
                this._priorityPanelParentMap.delete(type);
            }
        });
        this._loadPanelList.length = 0;
    }

    /**
     * 清除所有
     */
    private _clearAll () {
        this._priorityPanelParentMap.forEach((node, type) => {
            node.removeFromParent();
        });
        this._priorityPanelParentMap.clear();
        this._loadPanelList.length = 0;
        this._loadDialogList.length = 0;
    }

    /**
     * 根据优先级生成指定节点
     * @param priority 
     */
    private _initPriorityNode (priority: number) {
        //@ts-ignore
        let name = UIManagerDefines.PriorityToName["" + priority];
        let node = new Node("uiPanel_" + name);
        node.parent = find("Canvas");
        this._addWidget(node);
        // node.setSiblingIndex(priority);
        this._priorityPanelParentMap.set(priority, node);

        // 需要排序
        let list: Node[] = [];
        let nodeMap = new Map;
        this._priorityPanelParentMap.forEach((value, key) => {
            nodeMap.set(value, key);
            list.push(value);
        });

        list = list.sort((o1, o2) => {
            return nodeMap.get(o1) - nodeMap.get(o2);
        });

        let length = find("Canvas")!.children.length - list.length;
        for (let i = 0; i < list.length; i++) {
            list[i].setSiblingIndex(i + length);
        }
    }

    /**
     * 根据优先级获取节点
     * @param priority
     * @returns 
     */
    public getUINodeByPriority (priority: number) {
        if (!this._priorityPanelParentMap.has(priority)) {
            this._initPriorityNode(priority);
        }
        return this._priorityPanelParentMap.get(priority);
    }

    /**
     * 显示面板
     * @param path 
     * @param args 
     * @returns 
     */
    public async showPanel (path: string | UIManagerDefines.PanelOptions, ...args: []) {
        let options: UIManagerDefines.PanelOptions = { priority: UIManagerDefines.PANELPRIORITY.FOUR, path: "" };
        if (typeof path == "string") {
            options.path = path;
        } else {
            let opt = path as UIManagerDefines.PanelOptions;
            for (const key in opt) {
                // @ts-ignore
                options[key] = opt[key];
            }
            path = options.path;
        }

        let panel: Node;
        if (this._panelMap.has(path)) {
            panel = this._panelMap.get(path)!.panel;
            panel.parent = this.getUINodeByPriority(options.priority!)!;
        }
        else {
            // 应该不存在同时打开某个面板的需求
            if (this._loadPanelList.indexOf(path) != -1) {
                return;
            }
            else {
                let maskNode: Node | null = null;
                if (options.blockInput) {
                    maskNode = new Node;
                    maskNode.parent = find("Canvas");
                    this._addWidget(maskNode);
                    maskNode.addComponent(BlockInputEvents);
                }

                this._loadPanelList.push(path);
                // 加载动画，跟异步阻塞
                panel = await ResourceUtil.createUI(path, this.getUINodeByPriority(options.priority!));
                maskNode && maskNode.removeFromParent();
                // 冗错判定
                let loadIndex = this._loadPanelList.indexOf(path);
                if (loadIndex != -1) {
                    this._loadPanelList.splice(this._loadPanelList.indexOf(path), 1);
                } else {
                    panel.removeFromParent();
                    return;
                }
            }
        }

        this._panelMap.set(path, { options, panel });
        panel.active = true;

        //  关闭比自己层级高的面板
        if (options.closeOther) {
            this._priorityPanelParentMap.forEach((node, priority) => {
                if (priority == UIManagerDefines.PANELPRIORITY.TIPS
                    || priority == UIManagerDefines.PANELPRIORITY.DIALOG
                    || priority == UIManagerDefines.PANELPRIORITY.MARQUEE) {
                    return;
                }
                if (priority > options.priority!) {
                    node.removeAllChildren();
                }
            })
        }

        options.loadedCall && options.loadedCall();
        let component = panel!.getComponent(UIBase);
        if (component) {
            await component.show.apply(component, args);
        } else {
            throw `查找不到脚本文件 UIBase`;
        }
        options.enableCall && options.enableCall();

        component.setDisableCall(() => {
            options.disableCall && options.disableCall();
            this.emit(UIManagerDefines.UIEvent.ON_CLOSE_PANEL, path);
        });

        this.emit(UIManagerDefines.UIEvent.ON_OPEN_PANEL, path);
        return panel;
    }

    /**
     * 显示跑马灯
     * @param content 内容或配置
     * @param args  额外变量
     */
    public showMarquee (content: string | UIManagerDefines.MarqueeOptions, ...args: any) {
        // 默认值设定
        let options: UIManagerDefines.MarqueeOptions = { content: "", path: UIManagerDefines.DEFAULT_PATH.MARQUEE, playTime: 1, top: false };
        if (typeof content == "string") {
            options.content = content;
        } else {
            let opt = content as UIManagerDefines.MarqueeOptions;
            for (const key in opt) {
                // @ts-ignore
                options[key] = opt[key];
            }
            content = options.content;
        }

        // 设置额外变量
        options.args = args;

        if (!this._marqueePanel) {
            let panel = this.getUINodeByPriority(UIManagerDefines.PANELPRIORITY.MARQUEE);
            this._marqueePanel = panel!.addComponent(UIMarqueePanel);
        }
        this._marqueePanel.addMarquee(options);
    }

    /**
     * 显示弹窗
     * @param content { string | DialogOptions } 内容或者配置
     * @param args  额外变量
     * @returns 
     */
    public async showDialog (content: string | UIManagerDefines.DialogOptions, ...args: any) {
        // 默认值设定
        let options: UIManagerDefines.DialogOptions = { content: "", path: UIManagerDefines.DEFAULT_PATH.DIALOG };
        let argList: any[] = [];
        if (typeof content == "string") {
            options.content = content;
            argList.push(content)
        } else {
            let opt = content as UIManagerDefines.DialogOptions;
            for (const key in opt) {
                // @ts-ignore
                options[key] = opt[key];
            }
            content = options.content;
            argList.push(options)
        }

        let uuid = "dialog_" + this._index++;
        let path = options.path!;

        this._loadDialogList.push(uuid);
        let dialog: Node = await ResourceUtil.createUI(path, this.getUINodeByPriority(UIManagerDefines.PANELPRIORITY.DIALOG));

        // 冗错判定
        let loadIndex = this._loadDialogList.indexOf(uuid);
        if (loadIndex != -1) {
            this._loadDialogList.splice(loadIndex, 1)
        } else {
            dialog.removeFromParent();
            return;
        }

        this._dialogMap.set(uuid, dialog);

        let component = dialog!.getComponent(UIDialogBase);
        if (component) {
            if (args && args.length > 0) {
                argList = argList.concat(args)
            }
            //@ts-ignore
            await component.show.apply(component, argList);
        } else {
            throw `查找不到脚本文件 UIBase`;
        }

        component.setDisableCall((chosse: boolean) => {
            this._dialogMap.delete(uuid);
            this.emit(UIManagerDefines.UIEvent.ON_CLOSE_DIALOG, uuid);
        });

        this.emit(UIManagerDefines.UIEvent.ON_OPEN_DIALOG, uuid);
        return uuid;
    }

    /**
     * 显示提示
     * @param content 文字
     * @param icon 图标
     * @param callback 
     */
    public async showTips (content: string | UIManagerDefines.TipOptions, ...args: any) {
        // 默认值设定
        let options: UIManagerDefines.TipOptions = { content: "", path: UIManagerDefines.DEFAULT_PATH.TIP };
        if (typeof content == "string") {
            options.content = content;
        } else {
            let opt = content as UIManagerDefines.TipOptions;
            for (const key in opt) {
                // @ts-ignore
                options[key] = opt[key];
            }
            content = options.content;
        }

        let path = options.path!;
        let tipsNode: Node = await ResourceUtil.createUI(path, this.getUINodeByPriority(UIManagerDefines.PANELPRIORITY.TIPS));

        let tipScript = tipsNode.getComponent(UITipsBase) as UITipsBase;
        let argList = [content];
        if (args && args.length > 0) {
            argList = argList.concat(args)
        }
        //@ts-ignore
        tipScript.show.apply(tipScript, argList);
    }

    /**
     * 清除面板
     * @param flag 
     * @param path   MARQUEE 跟 TIPS 属于清空,  panel根据path清理  dialog根据id清理
     */
    public clean (flag: number = UIManagerDefines.UITYPE.ALL, path: string) {
        if (flag == UIManagerDefines.UITYPE.ALL) {
            this._clearPanels();
        } else if (flag & UIManagerDefines.UITYPE.PANEL) {
            if (this._panelMap.has(path)) {
                let panel = this._panelMap.get(path)?.panel;
                panel?.removeFromParent();
                this._panelMap.delete(path)
            } else {
                let loadIndex = this._loadPanelList.indexOf(path);
                if (loadIndex != -1) {
                    this._loadPanelList.splice(loadIndex, 1);
                }
            }
        } else if (flag & UIManagerDefines.UITYPE.MARQUEE) {
            let node = this.getUINodeByPriority(UIManagerDefines.UITYPE.MARQUEE);
            if (node) {
                let component = node.getComponent(UIMarqueePanel);
                component!.clear();
            }
        } else if (flag & UIManagerDefines.UITYPE.TIPS) {
            let node = this.getUINodeByPriority(UIManagerDefines.UITYPE.TIPS);
            if (node) {
                node.removeAllChildren();
            }
        } else if (flag & UIManagerDefines.UITYPE.DIALOG) {
            if (this._dialogMap.has(path)) {
                let dialog = this._dialogMap.get(path);
                dialog?.removeFromParent();
                this._dialogMap.delete(path)
            } else {
                let loadIndex = this._loadDialogList.indexOf(path);
                if (loadIndex != -1) {
                    this._loadDialogList.splice(loadIndex, 1);
                }
            }
        }
    }

    /**
     * 检查当前UI是否正在展示
     * @param path  (仅支持 panel 或者 dialog)
     */
    public checkUIIsVisiable (path: string) {
        if (this._dialogMap.has(path)) {
            let dialog = this._dialogMap.get(path);
            return isValid(dialog) && dialog!.active && isValid(dialog!.parent);
        }

        if (this._panelMap.has(path)) {
            let panel = this._panelMap.get(path)?.panel;
            return isValid(panel) && panel!.active && isValid(panel!.parent);
        }
        return false;
    }

    /**
     * 添加widget组件
     * @param node 
     */
    private _addWidget (node: Node) {
        let widget = node.addComponent(Widget);
        widget.alignFlags = 45;
        widget.left = 0;
        widget.right = 0;
        widget.top = 0;
        widget.bottom = 0;
        widget.updateAlignment();
    }

    // 监听事件相关
    /**
     * 监听事件
     * @param type 
     * @param callback 
     * @param target 
     */
    public on (type: string | UIManagerDefines.UIEvent, callback: Function, target?: any) {
        this._eventProcessor.on(type, callback, target);
    }

    /**
     *  取消监听事件
     * @param type 
     * @param callback 
     * @param target 
     */
    public off (type: string | UIManagerDefines.UIEvent, callback: Function, target?: any) {
        this._eventProcessor.off(type, callback, target);
    }

    /**
     * 监听一次
     * @param type 
     * @param callback 
     * @param target 
     */
    public once (type: string | UIManagerDefines.UIEvent, callback: Function, target?: any) {
        this._eventProcessor.once(type, callback, target);
    }

    /**
     * 派发事件
     * @param type 
     * @param arg0 
     * @param arg1 
     * @param arg2 
     * @param arg3 
     * @param arg4 
     */
    public emit (type: string, arg0?: any, arg1?: any, arg2?: any, arg3?: any, arg4?: any) {
        this._eventProcessor.dispatchEvent(type, arg0, arg1, arg2, arg3, arg4);
    }
}
